# -*- coding: utf-8 -*-
"""
    lantz.foreign
    ~~~~~~~~~~~~~

    Implements classes and methods to interface SR830 by serial connection
    Actions are written for some compilance testing of the instrument
    
    TODO: implement GPIB connection

    :copyright: (c) 2012 by Lantz Authors, see AUTHORS for more details.
    :license: BSD, see LICENSE for more details.
"""
from lantz import Action, Feat, DictFeat
from lantz.serial import SerialDriver
from lantz.errors import InstrumentError
from time import sleep
from lantz import Q_

class SR830(SerialDriver):
    """drivers connecting to SR0830 lock-in via serial port.
    """

    ENCODING = 'ascii'

    RECV_TERMINATION = '\r'
    SEND_TERMINATION = '\r'
    
    TIMEOUT=10

    def Errors(self):
        """
        Query for command errors
        TODO decode errors bits
        """   
        return self.query('ERRS?')



    @Feat()
    def idn(self):
        """Help for a a_read_only_property
        """
        return self.query('*IDN?')
    
    
    """
    Sine Out related functions   
    """  
    @Feat(units='V', limits=(0.004,5,0.002))
    def Amplitude(self):
        """Get the sine out amplitude
        """
        return float(self.query('SLVL?'))

    @Amplitude.setter
    def Amplitude(self, value):
        self.send('SLVL {:.1f}'.format(value))


    @Feat(units='hertz', limits=(1E5,))
    def Frequency(self):
        """Get the sine out frequency
        """
        return float(self.query('FREQ?'))
        

    @Frequency.setter
    def Frequency(self, value):
#        print(self.query('*IDN?'))
        if self.ReferenceSource=='Internal':
            sleep(0.5)
            self.send('FREQ {:.1f}'.format(value))
        else: 
           raise InstrumentError
    """
    Reference input related functions   
    """
    @Feat(values={'External': 0, 'Internal': 1})
    def ReferenceSource(self):
        return int(self.query('FMOD?'))
        
    @ReferenceSource.setter
    def ReferenceSource(self, value):
        self.send('FMOD {}'.format(value))     

    """
    Input related functions   
    """
    @Feat(values={'A': 0, 'A-B': 1, 'I(1e6)': 2, 'I(1e8)': 3})
    def InputConfiguration(self):
        return int(self.query('ISRC?'))
        
    @InputConfiguration.setter
    def InputConfiguration(self, value):
        self.send('ISRC {}'.format(value))  
        
    @Feat(values={'AC': 0, 'DC': 1})
    def InputCoupling(self):
        return int(self.query('ICPL ?'))

    @InputCoupling.setter
    def InputCoupling(self, value):
        self.send('ICPL {}'.format(value))      

    @Feat(values={'Float': 0, 'Ground': 1})
    def InputShieldGrounding(self):
        return int(self.query('ICPL ?'))

    @InputShieldGrounding.setter
    def InputShieldGrounding(self, value):
        self.send('ICPL {}'.format(value)) 
        
    @Feat(values={'2 nV/fA' : 0, '5 nV/fA' : 1, '10 nV/fA' : 2,
                  '20 nV/fA' : 3, '50 nV/fA' : 4, '100 nV/fA' : 5,
                  '200 nV/fA' : 6, '500 nV/fA' : 7, '1 μV/pA' : 8,
                  '2 μV/pA' : 9, '5 μV/pA' : 10, '10 μV/pA' : 11,
                  '20 μV/fA' : 12, '50 μV/pA' : 13, '100 μV/pA' : 14,
                  '200 μV/pA' : 15, '500 μV/pA' : 16, '1 mV/nA' : 17,
                  '2 mV/nA' : 18, '5 mV/nA' : 19, '10 mV/nA' : 20, 
                  '20 mV/nA' : 21, '50 mV/nA' : 22, '100 mV/nA' : 23,
                  '200 mV/nA' : 24, '500 mV/nA' : 25,'1 V/μA' : 26})
    def Sensitivity(self):
        """Get sensitivity
        """
        return int(self.query('SENS ?'))

    @Sensitivity.setter
    def Sensitivity(self, value):
        """Set sensitivity
        """
        self.send('SENS {:d}'.format(value))
     

    
    """
    Functions to read results    
    """      
    @Feat(units='V', limits=(1E5,))
    def R(self):
        """Help for a a_read_only_property
        """
        return float(self.query('OUTP ? 3'))
        
    @Feat(units='degree', limits=(360,))
    def Theta(self):
        """Help for a a_read_only_property
        """
        return float(self.query('OUTP ? 4'))    
        
    @Feat(units='V', limits=(1E5,))
    def X(self):
        """Help for a a_read_only_property
        """
        return float(self.query('OUTP ? 1'))
        
    @Feat(units='V', limits=(1E5,))
    def Y(self):
        """Help for a a_read_only_property
        """
        return float(self.query('OUTP ? 2'))      
        
    @Action()
    def Reset(self):
        self.send('*RST')
        self.send('OUTX 0')
        sleep(1)
        
    @Action()
    def TestCMMR(self):
        """Perform CMMR self test
        """
        print('Connect the Sine Out to both the A and B inputs of the lock-in.')
        self.Reset()
        self.Frequency=Q_(100,'hertz')
        self.Amplitude=Q_(1,'V')
        self.InputConfiguration='A-B'
        self.Sensitivity='200 μV/pA'
        sleep(5)
        return self.R
    
    @Action()
    def TestInputDCOffset(self):
        """Perform Input DC offset test
        """
        print('Connect a 50Ω terminator to the A input. ')        
        self.Reset()
        self.Frequency=Q_(1,'hertz')
        self.Sensitivity='1 mV/nA'
        sleep(5)
        R1=self.R
        self.InputCoupling='DC'
        sleep(5)
        R2=self.R
        return  R1,R2   
      
    @Action()
    def TestInputNoise(self):
        """Perform Input noise self test
        """
        print('Connect a 50Ω termination to the A input.')
        self.Reset()
        self.Sensitivity='100 nV/fA'
        sleep(5)
        return self.X
        
    @Action()
    def TestSineOut(self):
        """Perform Sine Output test
        """
        print('Connect the Sine Out to the A  input.')
        self.Reset()
        self.Frequency=Q_(1e3,'hertz')
        ParamList= (1.000,'1 V/μA'),( 0.200,'200 mV/nA'),(0.050,'50 mV/nA'), (0.010,'10 mV/nA')
        FreqList=24,48,72,96
        Res=[]
        for (amp,sens) in ParamList:
            
            self.Amplitude=Q_(amp,'V')
            self.Sensitivity=sens
            sleep(3)
            r1=self.R
            err=((r1-Q_(amp,'V'))/Q_(amp,'V'))
            Res.append(err)
            print(amp,sens,err,r1)
            
        # prepare frequency flatness test
        self.Sensitivity='1 V/μA'
        self.Amplitude=Q_(1,'V')        
        
        for Freq in FreqList:
            self.Frequency=Freq*Q_(1e3,'hertz')
            sleep(3)
            err=self.R.magnitude
            Res.append(err-1)
            print(Freq,err)
        
        return Res
        
    @Action()
    def OutputXYToFile(self):
        """Perform a measurement of X and Y and append to a file
        """
        print('press ENTER key for each point and q + ENTER to quit...')
        with open("out.txt", "wt") as out_file:
            while True:
                choice = input("> ")
                res = format(self.X)+'\t'+format(self.Y)
                print(res)
                out_file.write(res + '\n')
                if choice == 'q' :
                    print("Data saved to out.txt...")
                    break
        return 0
    
        

if __name__ == '__main__':
    import argparse
    import lantz.log

    

    parser = argparse.ArgumentParser(description='Test SR 830')
    parser.add_argument('-i', '--interactive', action='store_true',
                        default=False, help='Show interactive GUI')
    parser.add_argument('-p', '--port', type=str, default='/dev/cu.usbserial',
                        help='Serial port to connect to')

    args = parser.parse_args()
    #lantz.log.log_to_screen(lantz.log.DEBUG)
    lantz.log.log_to_socket(lantz.log.DEBUG)
    with SR830(args.port) as inst:
        if args.interactive:
            from lantz.ui.qtwidgets import start_test_app
            start_test_app(inst)
        else:
            #  test code's are here
            print('Non interactive mode')
            #print(inst.TestSineOut())
            #print(inst.TestCMMR())
            #print(inst.TestInputNoise())
            #print(inst.TestInputDCOffset())
            inst.OutputXYToFile()